package com.ruoyi.business.service.impl;

import java.util.List;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.annotation.DataScope;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.business.mapper.TDeviceMapper;
import com.ruoyi.business.domain.TDevice;
import com.ruoyi.business.service.ITDeviceService;
import java.util.Collections;
import java.util.Comparator;
import java.util.stream.Collectors;

/**
 * 设备管理Service业务层处理
 * 
 * @author laoguo
 * @date 2025-08-12
 */
@Service
public class TDeviceServiceImpl implements ITDeviceService 
{
    @Autowired
    private TDeviceMapper tDeviceMapper;

    /**
     * 查询设备管理
     * 
     * @param deviceId 设备管理主键
     * @return 设备管理
     */
    @Override
    public TDevice selectTDeviceByDeviceId(Long deviceId)
    {
        return tDeviceMapper.selectTDeviceByDeviceId(deviceId);
    }

    /**
     * 查询设备管理列表
     * 
     * @param tDevice 设备管理
     * @return 设备管理
     */
    @Override
    @DataScope(deptAlias = "d", userAlias = "u")
    public List<TDevice> selectTDeviceList(TDevice tDevice)
    {
        return tDeviceMapper.selectTDeviceList(tDevice);
    }

    /**
     * 新增设备管理
     * 
     * @param tDevice 设备管理
     * @return 结果
     */
    @Override
    public int insertTDevice(TDevice tDevice)
    {
        tDevice.setCreateTime(DateUtils.getNowDate());
        return tDeviceMapper.insertTDevice(tDevice);
    }

    /**
     * 修改设备管理
     * 
     * @param tDevice 设备管理
     * @return 结果
     */
    @Override
    public int updateTDevice(TDevice tDevice)
    {
        tDevice.setUpdateTime(DateUtils.getNowDate());
        return tDeviceMapper.updateTDevice(tDevice);
    }

    /**
     * 批量删除设备管理
     * 
     * @param deviceIds 需要删除的设备管理主键
     * @return 结果
     */
    @Override
    public int deleteTDeviceByDeviceIds(Long[] deviceIds)
    {
        return tDeviceMapper.deleteTDeviceByDeviceIds(deviceIds);
    }

    /**
     * 删除设备管理信息
     * 
     * @param deviceId 设备管理主键
     * @return 结果
     */
    @Override
    public int deleteTDeviceByDeviceId(Long deviceId)
    {
        return tDeviceMapper.deleteTDeviceByDeviceId(deviceId);
    }

    @Override
    public List<TDevice> findNearbyDevices(Double longitude, Double latitude) {
        // 步骤1：从数据库查询出所有在线的设备
        // 这里我们不直接在数据库里计算距离，而是先把数据查到内存中再处理
        // 这种方式在设备数量不多时（几千台以内）性能是可以接受的
        TDevice queryParam = new TDevice();
        queryParam.setStatus("1"); // 只查找在线的设备
        List<TDevice> allOnlineDevices = tDeviceMapper.selectTDeviceList(queryParam);

        // 步骤2：在Java内存中计算每台设备到用户位置的距离
        for (TDevice device : allOnlineDevices) {
            if (device.getLongitude() != null && device.getLatitude() != null) {
                double distance = calculateDistance(
                        latitude, longitude,
                        device.getLatitude().doubleValue(), device.getLongitude().doubleValue()
                );
                // 我们需要一个地方存储计算出的距离，但不想修改数据库实体类
                // 这里我们利用 BaseEntity 自带的 params 这个Map来临时存储
                device.getParams().put("distance", distance);
            } else {
                // 对于没有坐标的设备，给一个无限大的距离，让它排在最后
                device.getParams().put("distance", Double.MAX_VALUE);
            }
        }

        // 步骤3：根据计算出的距离进行排序
        allOnlineDevices.sort(Comparator.comparing(device -> (Double) device.getParams().get("distance")));

        // 步骤4：只返回距离最近的前20台设备
        return allOnlineDevices.stream().limit(20).collect(Collectors.toList());
    }

    /**
     * 地球半径，单位：千米
     */
    private static final double EARTH_RADIUS = 6371.0;

    /**
     * 根据经纬度计算球面距离（Haversine公式）
     *
     * @param lat1 用户纬度
     * @param lon1 用户经度
     * @param lat2 设备纬度
     * @param lon2 设备经度
     * @return 两点间的距离，单位：千米
     */
    private double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
        double dLat = Math.toRadians(lat2 - lat1);
        double dLon = Math.toRadians(lon2 - lon1);
        lat1 = Math.toRadians(lat1);
        lat2 = Math.toRadians(lat2);

        double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
                Math.sin(dLon / 2) * Math.sin(dLon / 2) * Math.cos(lat1) * Math.cos(lat2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        return EARTH_RADIUS * c;
    }
}
